﻿#region Namespace References

using System;
using System.Collections.Generic;
using System.Reflection;
using DynConDevCorp.OpenArch.SmartData.Interfaces;
using DynConDevCorp.OpenArch.SmartData.Interfaces.Attributes;

#endregion

namespace DynConDevCorp.OpenArch.SmartData.Persistance
{
    /// <summary>
    /// 
    /// </summary>
    public class FieldMapEntry
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="FieldMapEntry"/> class.
        /// </summary>
        /// <param name="objectFieldName">Name of the object field.</param>
        /// <param name="persistanceFieldName">Name of the persistance field.</param>
        public FieldMapEntry(String objectFieldName, String persistanceFieldName)
        {
            ObjectFieldName = objectFieldName;
            PersistanceFieldName = persistanceFieldName;
        }

        #endregion

        #region Public Instance Properties

        /// <summary>
        /// Gets or sets the name of the database field.
        /// </summary>
        /// <value>The name of the database field.</value>
        public String PersistanceFieldName { get; private set; }

        /// <summary>
        /// Gets or sets the name of the object field.
        /// </summary>
        /// <value>The name of the object field.</value>
        public String ObjectFieldName { get; private set; }

        #endregion
    }

    /// <summary>
    /// 
    /// </summary>
    public class EntityFieldMap : List<FieldMapEntry>
    {
    }

    internal class PersistanceFieldMetadata
    {
        #region Constructors

        public PersistanceFieldMetadata(Type entityType, PropertyInfo propertyInfo)
        {
            EntityType = entityType;
            ObjectFieldName = propertyInfo.Name;
            DatabaseFieldName = propertyInfo.Name;
            IsDatabaseField = true;
            IsXmlPersistable = true;
        }

        public PersistanceFieldMetadata(Type entityType, PropertyInfo propertyInfo, PersistAttribute attribute)
        {
            EntityType = entityType;
            ObjectFieldName = propertyInfo.Name;
            DatabaseFieldName = attribute.DatabaseFieldName;
            IsDatabaseField = attribute.IsDatabaseField;
            IsXmlPersistable = attribute.IsDatabaseField;
        }

        #endregion

        #region Public Instance Properties

        public string DatabaseFieldName { get; private set; }
        public Type EntityType { get; private set; }
        public Boolean IsDatabaseField { get; private set; }
        public Boolean IsXmlPersistable { get; private set; }
        public string ObjectFieldName { get; private set; }

        #endregion
    }

    /// <summary>
    /// 
    /// </summary>
    public class PersistanceMetadata
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PersistanceMetadata"/> class.
        /// </summary>
        /// <param name="entityType">Type of the entity.</param>
        internal PersistanceMetadata(Type entityType)
        {
            m_EntityType = entityType;
            m_PersistanceFieldMetadata = ProcessAttributes();
        }

        #endregion

        #region Public Instance Properties

        /// <summary>
        /// Gets the type of the entity.
        /// </summary>
        /// <value>The type of the entity.</value>
        public Type EntityType
        {
            get { return m_EntityType; }
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        /// Databases to object map.
        /// </summary>
        /// <returns></returns>
        public EntityFieldMap DatabaseToObjectMap()
        {
            EntityFieldMap retVal = new EntityFieldMap();
            foreach (PersistanceFieldMetadata meta in m_PersistanceFieldMetadata)
            {
                if (meta.IsDatabaseField && !String.IsNullOrEmpty(meta.ObjectFieldName) && !String.IsNullOrEmpty(meta.DatabaseFieldName))
                {
                    retVal.Add(new FieldMapEntry(meta.ObjectFieldName, meta.DatabaseFieldName));
                }
            }
            return retVal;
        }

        /// <summary>
        /// Objects to XML map.
        /// </summary>
        /// <returns></returns>
        public EntityFieldMap ObjectToXmlMap()
        {
            EntityFieldMap retVal = new EntityFieldMap();
            foreach (PersistanceFieldMetadata meta in m_PersistanceFieldMetadata)
            {
                if (meta.IsXmlPersistable && !String.IsNullOrEmpty(meta.ObjectFieldName))
                {
                    retVal.Add(new FieldMapEntry(meta.ObjectFieldName, meta.ObjectFieldName));
                }
            }
            return retVal;
        }

        #endregion

        #region Private Instance Methods

        private List<PersistanceFieldMetadata> ProcessAttributes()
        {
            List<PersistanceFieldMetadata> retVal = new List<PersistanceFieldMetadata>();
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            PropertyInfo[] allProperties = m_EntityType.GetProperties(flags);
            foreach (PropertyInfo property in allProperties)
            {
                object[] attributes = property.GetCustomAttributes(typeof (PersistAttribute), true);
                if (attributes.Length == 1)
                {
                    PersistAttribute persistAttribute = attributes[0] as PersistAttribute;
                    PersistanceFieldMetadata persistanceFieldMetadata = new PersistanceFieldMetadata(m_EntityType, property, persistAttribute);
                    retVal.Add(persistanceFieldMetadata);
                }
                else
                {
                    if (typeof(ISmartField).IsAssignableFrom(property.PropertyType))
                    {
                        PersistanceFieldMetadata persistanceFieldMetadata = new PersistanceFieldMetadata(m_EntityType, property);
                        retVal.Add(persistanceFieldMetadata);
                    }
                }
            }
            return retVal;
        }

        #endregion

        #region Fields and Constants

        private readonly Type m_EntityType;
        private readonly List<PersistanceFieldMetadata> m_PersistanceFieldMetadata;

        #endregion
    }
}